﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using AdministradorGeneral.Seguridad;
using CapaDatos;
using CapaDatos.AdministradorGeneral;
using CapaDatos.Contabilidad;
using Entidades;

namespace Siscont.Contabilidad
{
    /// <summary>
    /// Se encarga de gestionar el módulo de contabilidad.
    /// </summary>
    public class AdministradorContabilidad
    {

        #region Atributos

        private int situacionIva;
        public int SituacionIva
        {
            get { return situacionIva; }
            set { situacionIva = value; }
        }

        public string Usuario
        {
            get { return ModuloSoporte.Global.Usuario; }
            set 
            {
                ModuloSoporte.Global.Usuario = value; 
            }
        }

        long codEmpresa = 0;
        public long CodEmpresa
        {
            get { return codEmpresa; }
            set { codEmpresa = value; }
        }

        int codSucursal = 0;
        public int CodSucursal
        {
            get { return codSucursal; }
            set { codSucursal = value; }
        }

        int codEjercicio = 0;
        public int CodEjercicio
        {
            get { return codEjercicio; }
            set { codEjercicio = value; }
        }

        string codPuesto = "";
        public string CodPuesto
        {
            get { return codPuesto; }
            set { codPuesto = value; }
        }

        Permisos permisos = Permisos.PermisosGuest;
        public Permisos Permisos
        {
            get { return permisos; }
            set { permisos = value; }
        }

        public bool Logueado
        {
            get { return !string.IsNullOrEmpty(Usuario); }
        }

        #endregion

        #region Ctor

        public AdministradorContabilidad()
        {

        }

        #endregion

        #region Métodos que no requieren Login

        public IList ObtenerSucursales(long codEmpresa)
        {
            var ds = DatosAdministradorGeneral.ObtenerListaSucursales(codEmpresa);
            return (IList)Datos.GetTotalValores(ds);
        }

        public IList ObtenerListaEmpresas(string usuario)
        {
            var ds = DatosAdministradorGeneral.ObtenerListaEmpresas(usuario);
            return (IList)Datos.GetTotalValores(ds);
        }

        public IList ObtenerEjercicios(long codEmpresa)
        {
            var ds = DatosAdministradorGeneral.ObtenerEjercicios(codEmpresa);
            return (IList)Datos.GetTotalValores(ds);
        }

        public IList ObtenerPuestosDeTrabajo(long codEmpresa)
        {
            var ds = DatosAdministradorGeneral.ObtenerPuestosDeTrabajo(codEmpresa);
            return (IList)Datos.GetTotalValores(ds);
        }

        public void Acceder(long codEmpresa, long codSucursal, int codEjercicio, string codPuesto, int situacionIva)
        {
            var ds = DatosContabilidad.Acceder(codEmpresa, codSucursal, codEjercicio, codPuesto);

            if (Datos.EstaVacio(ds))
                throw new Exception("No se ha podido loguear, debido a que algún dato es incorrecto.");

            this.CodEjercicio = codEjercicio;
            this.CodEmpresa = codEmpresa;
            this.CodPuesto = codPuesto;
            this.CodSucursal = codEjercicio;
            this.SituacionIva = situacionIva;

            ModuloSoporte.Global.NombreEmpresa = ds.Tables[0].Rows[0].ItemArray[1].ToString();

            var permisos = new Entity_empresa_has_usuarios
            {
                Empresa_idempresa = codEmpresa,
                Usuarios_usuario = this.Usuario
            };
            permisos = (Entity_empresa_has_usuarios)Datos.Phantom.Cargar(permisos);

            this.Permisos = new Permisos { Grado=permisos.Permisos};
        }

        #endregion

        #region Listados
   
        public object[] ObtenerListaUnidades()
        {
            DataSet ds = DatosContabilidad.ObtenerListaUnidades(CodEmpresa);
            object[] datos = new object[Datos.GetTotalValores(ds, 0).Length];
            int i = 0;
            foreach (object[] o in Datos.GetTotalValores(ds))
            {
                datos[i++] = new object[] { o[0], o[1] };
            }

            return datos;
        }

        public object[] ObtenerListaCuentas()
        {
            DataSet ds = DatosContabilidad.ObtenerListaCuentas(CodEmpresa, CodEjercicio);
            object[] datos = new object[Datos.GetTotalValores(ds, 0).Length];
            int i = 0;
            foreach (object[] o in Datos.GetTotalValores(ds))
            {
                datos[i++] = new object[] { o[0], o[0].ToString() + ". " + o[1].ToString() };
            }

            return datos;
        }

        /// <summary>
        /// Carga un listado de cuentas imputables, en formato de 3 columnas.
        /// </summary>
        /// <returns></returns>
        public object[] ObtenerListaCuentas3()
        {
            DataSet ds = DatosContabilidad.ObtenerListaCuentasImputables(CodEmpresa, CodEjercicio);
            object[] datos = new object[Datos.GetTotalValores(ds, 0).Length];
            int i = 0;
            foreach (object[] o in Datos.GetTotalValores(ds))
            {
                datos[i++] = new object[] { o[0], o[1], o[0].ToString() + ". " + o[1].ToString() };
            }

            return datos;
        }

        public object[] ObtenerListaTiposMovimientos()
        {
            DataSet ds = DatosContabilidad.ObtenerListaTiposMovimientos(CodEmpresa);
            object[] datos = new object[Datos.GetTotalValores(ds, 0).Length];
            int i = 0;
            foreach (object o in Datos.GetTotalValores(ds, 0))
            {
                datos[i++] = new object[] { o.ToString() };
            }

            return datos;
        }

        /// <summary>
        /// Obtiene el código y la razón social de todos los clientes de la empresa logueada.
        /// </summary>
        /// <returns></returns>
        public object[] ObtenerListaClientes()
        {
            DataSet ds = DatosContabilidad.ObtenerListaClientes(CodEmpresa);
            object[] datos = new object[Datos.GetTotalValores(ds, 0).Length];
            int i = 0;
            foreach (object[] o in Datos.GetTotalValores(ds))
            {
                datos[i++] = new object[] { o[0].ToString(), o[1].ToString() };
            }

            return datos;
        }

        /// <summary>
        /// Obtiene el código y la razón social de todos los proveedores de la empresa logueada.
        /// </summary>
        /// <returns></returns>
        public object[] ObtenerListaProveedores()
        {
            DataSet ds = DatosContabilidad.ObtenerListaProveedores(CodEmpresa);
            object[] datos = new object[Datos.GetTotalValores(ds, 0).Length];
            int i = 0;
            foreach (object[] o in Datos.GetTotalValores(ds))
            {
                datos[i++] = new object[] { o[0].ToString(), o[1].ToString() };
            }

            return datos;
        }

        #endregion

        #region Libros

        /// <summary>
        /// Obtiene los asientos que serán listados en el libro diario.
        /// </summary>
        /// <param name="tipos">Tipos. En caso de ser null, lista todos los tipos.</param>
        /// <param name="desde"></param>
        /// <param name="hasta"></param>
        /// <returns></returns>
        public List<Asiento> ConsultaLibroDiario(string [] tipos, DateTime desde, DateTime hasta)
        {
            StringBuilder sbWhere = new StringBuilder();

            // Filtro "Fecha"
            if (desde != null)
            {
                sbWhere.Append("(");
                sbWhere.Append("fecha >= ");
                sbWhere.Append("'");
                sbWhere.Append(desde.ToString("yyyy-MM-dd"));
                sbWhere.Append("')");
            }

            if (hasta != null)
            {
                sbWhere.Append("(");
                sbWhere.Append("fecha <= ");
                sbWhere.Append("'");
                sbWhere.Append(hasta.ToString("yyyy-MM-dd"));
                sbWhere.Append("')");
            }

            // Filtro "Tipo de asiento"
            if (tipos != null)
            {
                sbWhere.Append("(tipoasiento_descripcion = '");
                sbWhere.Append(tipos[0]);
                for(int i = 1; i < tipos.Length; i++)
                {
                    sbWhere.Append("' or tipoasiento_descripcion = '");
                    sbWhere.Append(tipos[i]);
                }
                sbWhere.Append("')");
            }

            // Ejercicio correspondiente
            sbWhere.Append("(");
            sbWhere.Append("ejercicio_codejercicio = ");
            sbWhere.Append(CodEjercicio);
            sbWhere.Append(" AND ejercicio_empresa_idempresa = ");
            sbWhere.Append(CodEmpresa);
            sbWhere.Append(")");
            string where = sbWhere.ToString().Replace(")(", ") AND (");
            
            // Consulta (nueva)
            List<Asiento> asientos = new List<Asiento>();
            IList asientus = Datos.Phantom.Listar(typeof(Entity_asiento), where);
            foreach (Entity_asiento asiento in asientus)
            {
                asientos.Add(new Asiento(asiento));
            }
            return asientos;
        }
        #endregion

        #region Ejercicio

        public DateTime ObtenerInicioEjercicio()
        {
            return DateTime.Parse(Datos.GetValor(
                    DatosContabilidad.ObtenerInicioEjercicio(
                        this.CodEjercicio, this.CodEmpresa)).ToString());
        }

        public DateTime ObtenerFinEjercicio()
        {
            return DateTime.Parse(Datos.GetValor(
                    DatosContabilidad.ObtenerFinEjercicio(
                        this.CodEjercicio, this.CodEmpresa)).ToString());
        }

        #endregion

        #region Asientos

        /// <summary>
        /// Registra el asiento, sus movimientos, y devuelve su ID.
        /// </summary>
        /// <param name="descripcion"></param>
        /// <param name="tipo"></param>
        /// <param name="fecha"></param>
        /// <param name="editable"></param>
        /// <param name="habilitado"></param>
        /// <param name="sistema"></param>
        /// <param name="premovimientos"></param>
        /// <returns></returns>
        public int GenerarAsiento(
            string descripcion,
            string tipo,
            DateTime fecha,
            bool editable,
            bool habilitado,
            PreMovimiento[] premovimientos)
        {
            if (premovimientos.Length == 0)
            {
                throw new Exception("El asiento no se ha podido registrar debido a que no tiene movimientos asignados.");
            }

            Entity_asiento datos = new Entity_asiento();
            datos.Habilitado = habilitado;
            datos.Fecha = fecha;
            datos.Descripcion = descripcion;
            datos.Editable = editable;
            datos.Ejercicio_codejercicio = (int)this.CodEjercicio;
            datos.Ejercicio_empresa_idempresa = this.CodEmpresa;

            var movimientos = new List<Movimiento>(premovimientos.Length);
            int i = 0; // Lo dejo para la edición, y para compatibilidad con anterior agregar asiento
            foreach (PreMovimiento m in premovimientos)
            {
                Movimiento mov = new Movimiento(m);
                mov.Cod = ++i;
                mov.Datos.Asiento_ejercicio_codejercicio = this.CodEjercicio;
                mov.Datos.Asiento_ejercicio_empresa_idempresa = this.CodEmpresa;
                mov.Datos.Cuenta_idcuenta = m.Cuenta;
                mov.Datos.Cuenta_ejercicio_codejercicio = this.CodEjercicio;
                mov.Datos.Cuenta_ejercicio_empresa_idempresa = this.CodEmpresa;
                mov.Datos.Tipomovimiento_empresa_idempresa = this.CodEmpresa;
                movimientos.Add(mov);
            }

            Asiento asiento = new Asiento(datos);
            asiento.Movimientos = movimientos;
            if (!asiento.Balanceado)
            {
                throw new Exception("El asiento no se ha podido registrar debido a que no se encuentra balanceado.");
            }

            Gestor<Entity_asiento>.Insertar(datos);

            try
            {
                foreach (Movimiento m in asiento.Movimientos)
                {
                    m.Datos.Asiento_idasiento = datos.Idasiento;
                    Gestor<Entity_movimiento>.Insertar(m.Datos);
                }
            }
            catch (Exception ex)
            {
                asiento.Movimientos = null;
                Gestor<Entity_asiento>.Eliminar(datos);
                throw ex;
            }

            return datos.Idasiento;
        }

        /// <summary>
        /// Registra el asiento, sus movimientos, y devuelve su ID.
        /// </summary>
        /// <param name="descripcion"></param>
        /// <param name="fecha"></param>
        /// <param name="premovimientos"></param>
        /// <returns></returns>
        public int GenerarAsiento(
            string descripcion,
            DateTime fecha,
            PreMovimiento[] premovimientos)
        {
            if (premovimientos.Length == 0)
            {
                throw new Exception("El asiento no se ha podido registrar debido a que no tiene movimientos asignados.");
            }

            Entity_asiento datos = new Entity_asiento();
            datos.Habilitado = true;
            datos.Fecha = fecha;
            datos.Descripcion = descripcion;
            datos.Editable = true;
            datos.Ejercicio_codejercicio = this.CodEjercicio;
            datos.Ejercicio_empresa_idempresa = this.CodEmpresa;

            var movimientos = new List<Movimiento>(premovimientos.Length);
            //int i = 0;
            foreach (var m in premovimientos)
            {
                var mov = new Movimiento(m);
                //mov.Cod = ++i;
                mov.Datos.Asiento_ejercicio_codejercicio = this.CodEjercicio;
                mov.Datos.Asiento_ejercicio_empresa_idempresa = this.CodEmpresa;
                mov.Datos.Cuenta_idcuenta = m.Cuenta;
                mov.Datos.Cuenta_ejercicio_codejercicio = this.CodEjercicio;
                mov.Datos.Cuenta_ejercicio_empresa_idempresa = this.CodEmpresa;
                mov.Datos.Tipomovimiento_empresa_idempresa = this.CodEmpresa;
                //mov.Datos.Descripcion = m.Descripcion; // Lo hace en el constructor
                movimientos.Add(mov);
            }

            Asiento asiento = new Asiento(datos);
            asiento.Movimientos = movimientos;
            if (!asiento.Balanceado)
            {
                throw new Exception("El asiento no se ha podido registrar debido a que no se encuentra balanceado.");
            }

            Gestor<Entity_asiento>.Insertar(datos);

            try
            {
                foreach (Movimiento m in asiento.Movimientos)
                {
                    m.Datos.Tipomovimiento_descripcion = "Ajustes"; //HARDCODED
                    m.Datos.Asiento_idasiento = datos.Idasiento;
                    Gestor<Entity_movimiento>.Insertar(m.Datos);

                }
            }
            catch (Exception ex)
            {
                asiento.Movimientos = null;
                Gestor<Entity_asiento>.Eliminar(datos);
                throw ex;
            }

            return datos.Idasiento;
        }

        #endregion

        #region Usuario

        /// <summary>
        /// Limpia los datos del usuario.
        /// </summary>
        public void CerrarSesion()
        {
            this.Usuario = "";
            this.Permisos = Permisos.PermisosGuest;
            this.CodEjercicio = 0;
            this.CodEmpresa = 0;
            this.CodPuesto = "";
        }

        #endregion

        #region Cuentas

        /// <summary>
        /// Obtiene la cuenta padre de una cuenta en el plan de cuentas.
        /// </summary>
        /// <param name="nodeID"></param>
        /// <returns></returns>
        public Cuenta ObtenerCuentaPadre(string nodeID)
        {
            Entity_cuenta datosPadre = new Entity_cuenta 
            { 
                Idcuenta = nodeID, 
                Ejercicio_codejercicio = this.codEjercicio, 
                Ejercicio_empresa_idempresa = this.codEmpresa 
            };
            return new Cuenta(Gestor<Entity_cuenta>.Cargar(datosPadre));
        }

        /// <summary>
        /// Obtiene las cuentas hijas de otra cuenta en el plan de cuentas.
        /// </summary>
        /// <param name="padre"></param>
        /// <returns></returns>
        public List<Cuenta> ObtenerCuentas(Cuenta padre)
        {
            var tipo = typeof(Entidades.Entity_cuenta);
            var lista = CapaDatos.Datos.Phantom.Listar(tipo,
                    string.Format("ejercicio_empresa_idempresa={0} AND ejercicio_codejercicio={1} AND nivel={2} AND idcuenta LIKE '{3}%' ORDER BY idcuenta, descripcion",
                    CodEmpresa,
                    CodEjercicio,
                    padre.Nivel+1,
                    padre.Id));
            var retorno = new List<Cuenta>(lista.Count);
            foreach (var c in new List<Entity_cuenta>(lista.Cast<Entity_cuenta>()))
            {
                retorno.Add(new Cuenta(c));
            }

            return retorno;
        }

        /// <summary>
        /// Obtiene un listado de cuentas que será usado en el arbol del plan de cuentas.
        /// </summary>
        /// <returns></returns>
        public List<Cuenta> ObtenerCuentas(int nivel)
        {
            var tipo = typeof(Entidades.Entity_cuenta);
            var lista = CapaDatos.Datos.Phantom.Listar(tipo,
                    string.Format("ejercicio_empresa_idempresa={0} AND ejercicio_codejercicio={1} AND nivel={2} ORDER BY idcuenta, descripcion",
                    CodEmpresa,
                    CodEjercicio,
                    nivel));
            var retorno = new List<Cuenta>(lista.Count);
            foreach (var c in new List<Entity_cuenta>(lista.Cast<Entity_cuenta>()))
            {
                retorno.Add(new Cuenta(c));
            }

            return retorno;
        }

        /// <summary>
        /// Obtiene un listado de cuentas que será usado en el arbol del plan de cuentas.
        /// </summary>
        /// <returns></returns>
        public List<Entity_cuenta> ObtenerCuentas()
        {
            var tipo = typeof(Entidades.Entity_cuenta);
            var lista = CapaDatos.Datos.Phantom.Listar(tipo,
                    string.Format("ejercicio_empresa_idempresa={0} AND ejercicio_codejercicio={1} ORDER BY idcuenta, descripcion",
                    CodEmpresa,
                    CodEjercicio));
            return new List<Entity_cuenta>(lista.Cast<Entity_cuenta>());
        }

        #endregion

    }
}
